/* Copyright Thanh-Trung Pham, JAIST, 2013 */
package jp.ac.jaist.input;

import com.google.common.collect.Sets;
import jp.ac.jaist.*;
import jp.ac.jaist.BranchingStructureType.IfPart;
import jp.ac.jaist.ComponentConnectorType.ForService;
import jp.ac.jaist.ComponentType.RequiredService;
import jp.ac.jaist.FailureModelType.Input;
import jp.ac.jaist.FailureModelType.Input.Output;
import jp.ac.jaist.FailureModelType.Input.SignaledFailure;
import jp.ac.jaist.MultiTryCatchStructureType.MultiTryCatchPart;
import jp.ac.jaist.UsageProfilePartType.BCProbability;
import jp.ac.jaist.UsageProfilePartType.LCAverage;
import jp.ac.jaist.message.RMPIIMException;
import jp.ac.jaist.message.RMPIMessageResource;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.xmlbeans.*;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * @author Thanh-Trung Pham
 */
public class RMPIInputModeler {

    public static final Set<String> RMPI_F0_AS_SET = new HashSet<String>();
    private static final String RMP_F0 = "F0";
    private static final String RMP_ALL = "ALL";
    private static final String RMP_NONE = "NONE";
    private static final String RMP_SEMICOLON = "; ";
    private static final String RMP_DOT = ".";
    private static final String RMP_COMMA = ",";
    private static final String m_namespaceDeclaration = "declare namespace tns='jaist.ac.jp';";

    private boolean m_isVerifyingDuplicatedIANames = false;
    private String m_strInputModelAsString;
    //private SystemReliabilityModelingDocument m_oDocument;
    private XmlObject m_oXmlObject;
    private List<XmlError> m_oXMLErrors;
    private Set<String> m_oPropagatingFailureTypes;
    private Set<String> m_oStoppingFailureTypes;
    private Set<Set<String>> m_oAIOS;
    private Set<Set<String>> m_oAFS;

    private boolean m_isValid = false;

    private String m_strInputModelFilename;

    /**
     * The constructor.
     *
     * @throws RMPIIMException if there is an error.
     */
    private RMPIInputModeler() {

    }

    /**
     * Create a new instance.
     *
     * @return the newly created instance.
     */
    public static synchronized RMPIInputModeler newInstance() {
        return new RMPIInputModeler();
    }

    /**
     * Check if the input model is valid.
     *
     * @return true if the input model is valid, otherwise false.
     */
    public boolean isValid() {
        return m_isValid;
    }

    /**
     * Set the input model.
     *
     * @param inputModelAsString the input model as string.
     * @throws RMPIIMException if there is an error.
     */
    public void setInputModel(String inputModelAsString) throws RMPIIMException {
        m_strInputModelAsString = inputModelAsString;

        m_oAFS = null;
        m_oAIOS = null;

        try {
            initialize();
            m_isValid = true;
        } catch (RMPIIMException oEx) {
            m_isValid = false;
            m_oPropagatingFailureTypes = null;
            m_oStoppingFailureTypes = null;

            throw oEx;
        }

    }

    /**
     * Get an XML Line Number object for an XML object.
     *
     * @param xmlObject the XML object.
     * @return an XML Line Number object.
     */
    private XmlLineNumber getXMLLineNumber(XmlObject xmlObject) {
        XmlCursor oCursor = xmlObject.newCursor();
        XmlLineNumber oXmlLineNumber = (XmlLineNumber) oCursor.getBookmark(XmlLineNumber.class);
        if (oXmlLineNumber == null) {
            oXmlLineNumber = (XmlLineNumber) oCursor.toPrevBookmark(XmlLineNumber.class);
        }

        return oXmlLineNumber;
    }

    /**
     * Initialize.
     *
     * @throws RMPIIMException if there is an error.
     */
    private void initialize() throws RMPIIMException {
        m_oXMLErrors = new ArrayList<XmlError>();

        try {
            XmlOptions oXMLOptions = new XmlOptions();
            oXMLOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS);

            //m_oDocument = SystemReliabilityModelingDocument.Factory.parse(oFile, oXMLOptions);
            m_oXmlObject = XmlObject.Factory.parse(m_strInputModelAsString, oXMLOptions);

            validate();
        } catch (XmlException oXmlException) {
            throw new RMPIIMException(RMPIMessageResource.IMM_UNEXPECTED_EXCEPTION, oXmlException, false);
        }
    }

    /**
     * Validate.
     *
     * @throws RMPIIMException if there is an error.
     */
    private void validate() throws RMPIIMException {
        XmlOptions oValidateOptions = new XmlOptions();
        oValidateOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS);


        oValidateOptions.setErrorListener(m_oXMLErrors);

        //boolean isValid = m_oDocument.validate(oValidateOptions);
        boolean isValid = m_oXmlObject.validate(oValidateOptions);

        if (!isValid) {
            //m_oDocument = null;
            m_oXmlObject = null;

            throw new RMPIIMException(RMPIMessageResource.IMM_INVALID_INPUT_MODEL, false);
        }

        verifyFailureTypes();
        verifyServices();
        verifyComponents();
        verifySystemArchitecture();
        verifyUserInterfaces();
        verifyUsageProfile();

    }

    /**
     * Verify service implementations in a given component.
     *
     * @param component the given component.
     * @throws RMPIIMException if the service implements in the given component are invalid.
     */
    private void verifyServiceImplementations(ComponentType component, List<String> providedServiceNames, List<String> requiredServiceNames) throws RMPIIMException {
        String strComponentName = component.getName();

        ServiceImplementationType[] oSIs = component.getServiceImplementationArray();
        List<String> oListOfSINames = new ArrayList<String>();
        List<String> oListOfCalledServiceNames = new ArrayList<String>();

        for (ServiceImplementationType oSI : oSIs) {
            String strForService = oSI.getForService();

            if (oListOfSINames.contains(strForService)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_SERVICE_IMPLEMENTATION_IN_COMPONENT, strForService, strComponentName, false);
            }

            if (!providedServiceNames.contains(strForService)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_FOR_PROVIDED_SERVICE_IMPLEMENTATION_IN_COMPONENT, strForService, strComponentName, false);
            }

            oListOfCalledServiceNames.addAll(verifyServiceImplementation(oSI, strComponentName, requiredServiceNames));
            oListOfSINames.add(strForService);
        }

        for (String strProvidedServiceName : providedServiceNames) {
            if (!oListOfSINames.contains(strProvidedServiceName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_MISSING_SERVICE_IMPLEMENTATION_IN_COMPONENT, strProvidedServiceName, strComponentName, false);
            }
        }

        for (String strRequiredServiceName : requiredServiceNames) {
            if (!oListOfCalledServiceNames.contains(strRequiredServiceName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_REDUNDANT_REQUIRED_SERVICE_IN_COMPONENT, strRequiredServiceName, strComponentName, false);
            }
        }
    }

    /**
     * Verify the given service implementation.
     *
     * @param serviceImplementation the given service implementation.
     * @param componentName         the component name.
     * @param requiredServiceNames  the list of required service names in the given component.
     * @return a list of called service names of calling activities.
     * @throws RMPIIMException if the given service implementation is invalid.
     */
    private List<String> verifyServiceImplementation(ServiceImplementationType serviceImplementation, String componentName, List<String> requiredServiceNames) throws RMPIIMException {
        String strForService = serviceImplementation.getForService();
        List<String> oListOfIANames = new ArrayList<String>();

        List<String> oListOfCalledServiceNames = new ArrayList<String>();

        LinkedList<CallPropagationType> oTODOs = new LinkedList<CallPropagationType>();
        oTODOs.add(serviceImplementation);
        while (!oTODOs.isEmpty()) {
            CallPropagationType oTODO = oTODOs.removeFirst();
            if (oTODO.isSetInternalActivity()) {
                InternalActivityType oIA = oTODO.getInternalActivity();

                String strIAName = oIA.getName();
                if (m_isVerifyingDuplicatedIANames && oListOfIANames.contains(strIAName)) {
                    throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_INTERNAL_ACTIVITY_NAME_IN_SERVICE_IMPLEMENTATION_IN_COMPONENT, strIAName, strForService, componentName, false);
                }

                verifyIA(oIA);

                oListOfIANames.add(strIAName);
            } else if (oTODO.isSetCallingActivity()) {
                String strCalledService = oTODO.getCallingActivity().getCalledService();
                if (!requiredServiceNames.contains(strCalledService)) {
                    throw new RMPIIMException(RMPIMessageResource.IMM_INVALID_CALLING_ACTIVITY_IN_SERVICE_IMPLEMENTATION_IN_COMPONENT, strCalledService, strForService, componentName, false);
                }

                oListOfCalledServiceNames.add(strCalledService);
            } else if (oTODO.isSetBranchingStructure()) {
                BranchingStructureType oBranchingStructure = oTODO.getBranchingStructure();
                oTODOs.addAll(Arrays.asList(oBranchingStructure.getIfPartArray()));
                if (oBranchingStructure.getElsePart() != null) {
                    oTODOs.add(oBranchingStructure.getElsePart());
                }
            } else if (oTODO.isSetLoopingStructure()) {
                oTODOs.add(oTODO.getLoopingStructure().getLoopingPart());
            } else if (oTODO.isSetMultiTryCatchStructure()) {
                verifyMultiTryCatchStructure(oTODO.getMultiTryCatchStructure());
                oTODOs.addAll(Arrays.asList(oTODO.getMultiTryCatchStructure().getMultiTryCatchPartArray()));
            } else if (oTODO.isSetParallelStructure()) {
                oTODOs.addAll(Arrays.asList(oTODO.getParallelStructure().getParallelPartArray()));
            } else if (oTODO.isSetRetryStructure()) {
                verifyRetryStructure(oTODO.getRetryStructure());
                oTODOs.add(oTODO.getRetryStructure().getRetryPart());
            } else if (oTODO.isSetSequentialStructure()) {
                oTODOs.addAll(Arrays.asList(oTODO.getSequentialStructure().getSequentialPartArray()));
            } else if (oTODO.isSetMVPStructure()) {
                verifyMVPStructure(oTODO.getMVPStructure());
                oTODOs.addAll(Arrays.asList(oTODO.getMVPStructure().getMVPPartArray()));
            }
        }

        return oListOfCalledServiceNames;
    }

    /**
     * Verify a MVP Structure.
     *
     * @param mvpStructure the MVP Structure.
     * @throws RMPIIMException if the given MVP structure is invalid.
     */
    private void verifyMVPStructure(MVPStructureType mvpStructure) throws RMPIIMException {
        MVPStructureType.MVPPart[] oMVPParts = mvpStructure.getMVPPartArray();
        int iNumberOfMVPParts = oMVPParts.length;

        int iMinMS = mvpStructure.getMinMajoritySize();
        if ((iMinMS < Math.ceil(((double) (iNumberOfMVPParts + 1)) / 2)) || (iMinMS > iNumberOfMVPParts)) {
            XmlLineNumber oXMLLineNumber = getXMLLineNumber(mvpStructure);
            throw new RMPIIMException(RMPIMessageResource.IMM_MVP_STRUCTURE_INVALID_MIN_MAJORITY_SIZE, String.valueOf(iMinMS), String.valueOf(oXMLLineNumber.getLine()), false);
        }

        MVPStructureType.ErroneousOutput oErroneousOutput = mvpStructure.getErroneousOutput();
        String strInvolvedFailures = oErroneousOutput.getInvolvedFailures();
        if (RMP_F0.equals(strInvolvedFailures)) {
            XmlLineNumber oXMLLineNumber = getXMLLineNumber(oErroneousOutput);
            throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, RMP_F0, String.valueOf(oXMLLineNumber.getLine()), false);
        }

        String[] oInvolvedFailures = StringUtils.split(strInvolvedFailures, RMP_COMMA);

        Set<String> oSetOfInvolvedFailures = new HashSet<String>();
        for (String strInvolvedFailure : oInvolvedFailures) {
            if (RMP_F0.equals(strInvolvedFailure) || m_oStoppingFailureTypes.contains(strInvolvedFailure)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oErroneousOutput);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strInvolvedFailure, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (!m_oPropagatingFailureTypes.contains(strInvolvedFailure)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oErroneousOutput);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strInvolvedFailure, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (oSetOfInvolvedFailures.contains(strInvolvedFailure)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oErroneousOutput);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strInvolvedFailure, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oSetOfInvolvedFailures.add(strInvolvedFailure);
        }

        MVPStructureType.SignaledFailure oSignaledFailure = mvpStructure.getSignaledFailure();
        String strSignaledFailure = oSignaledFailure.getInvolvedFailure();
        if (RMP_F0.equals(strSignaledFailure) || m_oPropagatingFailureTypes.contains(strSignaledFailure)) {
            XmlLineNumber oXMLLineNumber = getXMLLineNumber(oSignaledFailure);
            throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strSignaledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
        }

        if (!m_oStoppingFailureTypes.contains(strSignaledFailure)) {
            XmlLineNumber oXMLLineNumber = getXMLLineNumber(oSignaledFailure);
            throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strSignaledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
        }

        verifyAgreementOfErrorsVector(mvpStructure.getAgreementOfErrorsVector(), iMinMS, iNumberOfMVPParts);

        MVPStructureType.ErrorPropertyVector[] oErrorPropertyVectors = mvpStructure.getErrorPropertyVectorArray();
        for (int i = 0; i < oErrorPropertyVectors.length; i++) {
            verifyErrorPropertyVector(oErrorPropertyVectors[i]);
        }
    }

    /**
     * Verify the error property vector.
     *
     * @param errorPropertyVector the error property vector.
     * @throws RMPIIMException if the error property vector is invalid.
     */
    private void verifyErrorPropertyVector(MVPStructureType.ErrorPropertyVector errorPropertyVector) throws RMPIIMException {
        Set<Set<String>> oSetOfSetOfOutputFailures = new HashSet<Set<String>>();

        for (MVPStructureType.ErrorPropertyVector.ForErroneousOutput oForErroneousOutput : errorPropertyVector.getForErroneousOutputArray()) {
            String strOutputFailures = oForErroneousOutput.getInvolvedFailures();

            if (RMP_F0.equals(strOutputFailures)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForErroneousOutput);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, RMP_F0, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            Set<String> oSetOfOutputFailures = new HashSet<String>();
            String[] oOutputFailures = StringUtils.split(strOutputFailures, RMP_COMMA);

            for (String strOutputFailure : oOutputFailures) {
                if (RMP_F0.equals(strOutputFailure) || m_oStoppingFailureTypes.contains(strOutputFailure)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForErroneousOutput);
                    throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strOutputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                if (!m_oPropagatingFailureTypes.contains(strOutputFailure)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForErroneousOutput);
                    throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strOutputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                if (oSetOfOutputFailures.contains(strOutputFailure)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForErroneousOutput);
                    throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strOutputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                oSetOfOutputFailures.add(strOutputFailure);
            }

            if (oSetOfSetOfOutputFailures.contains(oSetOfOutputFailures)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForErroneousOutput);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPES_SET, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oSetOfSetOfOutputFailures.add(oSetOfOutputFailures);
        }

    }


    /**
     * Verify the agreement of errors vector.
     *
     * @param agreementOfErrorsVector the agreement of errors vector.
     * @param minMajoritySize         the minimum majority size.
     * @param numberOfMVPParts        the number of MVPParts.
     * @throws RMPIIMException if the the agreement of errors vector is invalid.
     */
    private void verifyAgreementOfErrorsVector(MVPStructureType.AgreementOfErrorsVector agreementOfErrorsVector, int minMajoritySize, int numberOfMVPParts) throws RMPIIMException {
        List<Integer> oListOfForNumbers = new ArrayList<Integer>();

        for (MVPStructureType.AgreementOfErrorsVector.ForNumberOfErrors oForNumberOfErrors : agreementOfErrorsVector.getForNumberOfErrorsArray()) {
            int iForNumberOfErrors = oForNumberOfErrors.getForNumberOfErrors();

            if (iForNumberOfErrors < minMajoritySize || iForNumberOfErrors > numberOfMVPParts) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForNumberOfErrors);
                throw new RMPIIMException(RMPIMessageResource.IMM_MVP_STRUCTURE_INVALID_NUMBER_OF_ERRORS_IN_AGREEMENT_OF_ERRORS_VECTOR, String.valueOf(iForNumberOfErrors), String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (oListOfForNumbers.contains(iForNumberOfErrors)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForNumberOfErrors);
                throw new RMPIIMException(RMPIMessageResource.IMM_MVP_STRUCTURE_DUPLICATED_ELEMENT_IN_AGREEMENT_OF_ERRORS_VECTOR, String.valueOf(iForNumberOfErrors), String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfForNumbers.add(iForNumberOfErrors);
        }
    }

    /**
     * Verify an internal activity.
     *
     * @param oIA an internal activity.
     * @throws RMPIIMException if the internal activity is invalid.
     */
    private void verifyIA(InternalActivityType oIA) throws RMPIIMException {
        FailureModelType oFailureModel = oIA.getFailureModel();
        if (oFailureModel == null) {
            return;
        }

        Input[] oInputs = oFailureModel.getInputArray();

        Set<Set<String>> oSetOfSetOfInputFailures = new HashSet<Set<String>>();
        for (Input oInput : oInputs) {
            String strInputFailures = oInput.getInvolvedFailures();

            Set<String> oSetOfInputFailures = new HashSet<String>();
            if (RMP_F0.equals(strInputFailures)) {
                oSetOfInputFailures.add(RMP_F0);
            } else {
                String[] oInputFailures = StringUtils.split(strInputFailures, RMP_COMMA);

                for (String strInputFailure : oInputFailures) {
                    if (RMP_F0.equals(strInputFailure) || m_oStoppingFailureTypes.contains(strInputFailure)) {
                        XmlLineNumber oXMLLineNumber = getXMLLineNumber(oInput);
                        throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strInputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                    }

                    if (!m_oPropagatingFailureTypes.contains(strInputFailure)) {
                        XmlLineNumber oXMLLineNumber = getXMLLineNumber(oInput);
                        throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strInputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                    }

                    if (oSetOfInputFailures.contains(strInputFailure)) {
                        XmlLineNumber oXMLLineNumber = getXMLLineNumber(oInput);
                        throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strInputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                    }

                    oSetOfInputFailures.add(strInputFailure);
                }
            }

            if (oSetOfSetOfInputFailures.contains(oSetOfInputFailures)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oInput);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPES_SET, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oSetOfSetOfInputFailures.add(oSetOfInputFailures);

            double dTotalProbability = 0.0;
            SignaledFailure[] oSignaledFailures = oInput.getSignaledFailureArray();
            Set<String> oSetOfSignaledFailures = new HashSet<String>();

            for (SignaledFailure oSignaledFailure : oSignaledFailures) {
                String strSignaledFailure = oSignaledFailure.getInvolvedFailure();
                if (RMP_F0.equals(strSignaledFailure) || m_oPropagatingFailureTypes.contains(strSignaledFailure)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oSignaledFailure);
                    throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strSignaledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                if (!m_oStoppingFailureTypes.contains(strSignaledFailure)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oSignaledFailure);
                    throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strSignaledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                if (oSetOfSignaledFailures.contains(strSignaledFailure)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oSignaledFailure);
                    throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strSignaledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                oSetOfSignaledFailures.add(strSignaledFailure);

                dTotalProbability += oSignaledFailure.getProbability();
                if (dTotalProbability > 1.0) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oSignaledFailure);
                    throw new RMPIIMException(RMPIMessageResource.IMM_INVALID_INTERNAL_ACTIVITY_IN_SERVICE_IMPLEMENTATION_IN_COMPONENT, strInputFailures, String.valueOf(oXMLLineNumber.getLine()), false);
                }
            }


            Set<Set<String>> oSetOfSetOfOutputFailures = new HashSet<Set<String>>();
            Output[] oOutputs = oInput.getOutputArray();
            for (Output oOutput : oOutputs) {
                String strOutputFailures = oOutput.getInvolvedFailures();

                Set<String> oSetOfOutputFailures = new HashSet<String>();
                if (RMP_F0.equals(strOutputFailures)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oOutput);
                    throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                String[] oOutputFailures = StringUtils.split(strOutputFailures, RMP_COMMA);

                for (String strOutputFailure : oOutputFailures) {
                    if (RMP_F0.equals(strOutputFailure) || m_oStoppingFailureTypes.contains(strOutputFailure)) {
                        XmlLineNumber oXMLLineNumber = getXMLLineNumber(oOutput);
                        throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strOutputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                    }

                    if (!m_oPropagatingFailureTypes.contains(strOutputFailure)) {
                        XmlLineNumber oXMLLineNumber = getXMLLineNumber(oOutput);
                        throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strOutputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                    }

                    if (oSetOfOutputFailures.contains(strOutputFailure)) {
                        XmlLineNumber oXMLLineNumber = getXMLLineNumber(oOutput);
                        throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strOutputFailure, String.valueOf(oXMLLineNumber.getLine()), false);
                    }

                    oSetOfOutputFailures.add(strOutputFailure);
                }


                if (oSetOfSetOfOutputFailures.contains(oSetOfOutputFailures)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oOutput);
                    throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPES_SET, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                oSetOfSetOfOutputFailures.add(oSetOfOutputFailures);

                dTotalProbability += oOutput.getProbability();
                if (dTotalProbability > 1.0) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oOutput);
                    throw new RMPIIMException(RMPIMessageResource.IMM_INVALID_INTERNAL_ACTIVITY_IN_SERVICE_IMPLEMENTATION_IN_COMPONENT, strInputFailures, String.valueOf(oXMLLineNumber.getLine()), false);
                }
            }
        }
    }

    /**
     * Verify a multi try catch structure.
     *
     * @param multiTryCatchStructure a multi try catch structure.
     * @throws RMPIIMException if the given multi try catch structure is invalid.
     */
    private void verifyMultiTryCatchStructure(MultiTryCatchStructureType multiTryCatchStructure) throws RMPIIMException {
        MultiTryCatchPart[] oMultiTryCatchParts = multiTryCatchStructure.getMultiTryCatchPartArray();

        for (MultiTryCatchPart oMultiTryCatchPart : oMultiTryCatchParts) {
            verifyHandledFailures(oMultiTryCatchPart.getHandledFailures());
        }
    }

    /**
     * Verify a retry structure.
     *
     * @param retryStructure a retry structure.
     * @throws RMPIIMException if the given the given retry structure is invalid.
     */
    private void verifyRetryStructure(RetryStructureType retryStructure) throws RMPIIMException {
        verifyHandledFailures(retryStructure.getHandledFailures());
    }

    /**
     * Verify handled failures of a fault tolerance structure or signaled failures of a service.
     *
     * @param handledFailures handled failures or signaled failures.
     * @throws RMPIIMException if the given handled failures or signaled failures are invalid.
     */
    private void verifyHandledFailures(HandledFailuresType handledFailures) throws RMPIIMException {
        if (handledFailures == null) {
            return;
        }

        String strHandledFailures = handledFailures.getStringValue();
        if (RMP_NONE.equals(strHandledFailures) || RMP_ALL.equals(strHandledFailures)) {
            return;
        }

        String[] oHandledFailures = StringUtils.split(strHandledFailures, RMP_COMMA);

        List<String> oListOfHandledFailures = new ArrayList<String>();
        for (String strHandledFailure : oHandledFailures) {
            if (RMP_F0.equals(strHandledFailure) || RMP_ALL.equals(strHandledFailure) || RMP_NONE.equals(strHandledFailure) || m_oPropagatingFailureTypes.contains(strHandledFailure)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(handledFailures);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strHandledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (!m_oStoppingFailureTypes.contains(strHandledFailure)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(handledFailures);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_FAILURE_TYPE, strHandledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (oListOfHandledFailures.contains(strHandledFailure)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(handledFailures);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strHandledFailure, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfHandledFailures.add(strHandledFailure);
        }
    }

    /**
     * Verify the system architecture of the input model.
     *
     * @throws RMPIIMException if the system architecture are invalid.
     */
    private void verifySystemArchitecture() throws RMPIIMException {
        SystemArchitectureType oSA = getSystemArchitecture();
        HashMap<String, String> oComponentInstanceInfo = verifyComponentInstances(oSA);
        verifyComponentConnectors(oSA, oComponentInstanceInfo);
    }

    /**
     * Verify the usage profile of the input mode.
     *
     * @throws RMPIIMException if the usage profile is invalid.
     */
    private void verifyUsageProfile() throws RMPIIMException {
        UsageProfileType oUsageProfile = getUsageProfile();
        UsageProfilePartType[] oUsageProfileParts = oUsageProfile.getUsageProfilePartArray();

        double dEpsilon = 0.000000001;
        double dTotalProbability = 0.0;
        for (UsageProfilePartType oUsageProfilePart : oUsageProfileParts) {
            String strUPForUIName = oUsageProfilePart.getForUserInterface();
            UserInterfaceType oUserInterface = getUserInterfaceByName(strUPForUIName);
            if (oUserInterface == null) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oUsageProfilePart);
                throw new RMPIIMException(RMPIMessageResource.IMM_USAGE_PROFILE_PART_REFERRING_TO_NON_EXISTING_USER_INTERFACE, strUPForUIName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            dTotalProbability += oUsageProfilePart.getProbability();

            verifyLCAveragesAndLCProbabilityDistributions(oUsageProfilePart);
            verifyProbabilities(oUsageProfilePart);

            verifyBranchingConditionsAndLoopingCountsAndRecursiveCalls(oUsageProfilePart);
        }

        if (dTotalProbability < (1.0 - dEpsilon) || dTotalProbability > (1.0 + dEpsilon)) {
            XmlLineNumber oXMLLineNumber = getXMLLineNumber(oUsageProfile);
            throw new RMPIIMException(RMPIMessageResource.IMM_INVALID_USAGE_PROFILE, String.valueOf(oXMLLineNumber.getLine()), false);
        }
    }

    /**
     * Verify branching conditions, looping counts and recursive calls for a user interface with a given usage profile part.
     *
     * @param usageProfilePart the usage profile part.
     * @throws RMPIIMException if there are recursive calls, missing/invalid looping counts and missing/invalid branching conditions.
     */
    private void verifyBranchingConditionsAndLoopingCountsAndRecursiveCalls(UsageProfilePartType usageProfilePart) throws RMPIIMException {
        UserInterfaceType oUserInterface = getUserInterfaceByName(usageProfilePart.getForUserInterface());
        String strUIComponentInstanceName = oUserInterface.getFromService().getFromComponentInstance();
        String strUIProvidedServiceName = oUserInterface.getFromService().getFromProvidedService();

        List<String> oTrackedFullProvidedServices = new ArrayList<String>();

        verifyBranchingConditionsAndLoopingCountsAndRecursiveCalls(usageProfilePart, strUIComponentInstanceName, strUIProvidedServiceName, oTrackedFullProvidedServices);
    }

    /**
     * Verify branching conditions, looping counts and recursive calls for a provided service of a component instance with a given usage profile part.
     *
     * @param usageProfilePart            the usage profile part.
     * @param componentInstanceName       the component instance name.
     * @param providedServiceName         the provided service name.
     * @param trackedFullProvidedServices the list of tracked full provided services.
     * @throws RMPIIMException if there are recursive calls, missing/invalid looping counts and missing/invalid branching conditions.
     */
    private void verifyBranchingConditionsAndLoopingCountsAndRecursiveCalls(UsageProfilePartType usageProfilePart, String componentInstanceName, String providedServiceName, List<String> trackedFullProvidedServices) throws RMPIIMException {
        String strFullProvidedServiceName = componentInstanceName + RMP_DOT + providedServiceName;
        if (trackedFullProvidedServices.contains(strFullProvidedServiceName)) {
            throw new RMPIIMException(RMPIMessageResource.IMM_USER_INTERFACE_CONTAINING_RECURSIVE_CALLS, usageProfilePart.getForUserInterface(), providedServiceName, componentInstanceName, false);
        }

        trackedFullProvidedServices.add(strFullProvidedServiceName);

        ComponentInstanceType oComponentInstance = getComponentInstance(componentInstanceName);
        String strComponentName = oComponentInstance.getForComponent();
        ServiceImplementationType oServiceImplementation = getServiceImplementation(strComponentName, providedServiceName);

        verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oServiceImplementation, trackedFullProvidedServices);

        trackedFullProvidedServices.remove(strFullProvidedServiceName);
    }

    /**
     * Verify branching conditions and looping counts for a call propagation with a given usage profile part..
     *
     * @param usageProfilePart            the usage profile part.
     * @param componentInstanceName       the component instance name.
     * @param callPropagation             the call propagation.
     * @param trackedFullProvidedServices the list of tracked full provided services.e
     * @throws RMPIIMException if there are missing/invalid looping counts and missing/invalid branching conditions.
     */
    private void verifyBranchingConditionsAndLoopingCounts(UsageProfilePartType usageProfilePart, String componentInstanceName, CallPropagationType callPropagation, List<String> trackedFullProvidedServices) throws RMPIIMException {
        if (callPropagation.isSetBranchingStructure()) {
            BranchingStructureType oBranchingStructure = callPropagation.getBranchingStructure();
            IfPart[] oIfParts = oBranchingStructure.getIfPartArray();

            double dProbabilityOfAllIfBranches = 0.0;

            String strConditionLabels = StringUtils.EMPTY;

            for (IfPart oIfPart : oIfParts) {
                verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oIfPart, trackedFullProvidedServices);

                String strConditionLabel = oIfPart.getBranchingCondition();

                BCProbability oUsageProfilePartBCProbability = getUsageProfilePartBCProbability(usageProfilePart, strConditionLabel);
                if (oUsageProfilePartBCProbability == null) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(usageProfilePart);
                    throw new RMPIIMException(RMPIMessageResource.IMM_USAGE_PROFILE_PART_MISSING_BRANCHING_CONDITION_PROBABILITY, strConditionLabel, String.valueOf(oXMLLineNumber.getLine()), false);
                }

                dProbabilityOfAllIfBranches += oUsageProfilePartBCProbability.getValue();
                if (StringUtils.isEmpty(strConditionLabels)) {
                    strConditionLabels += strConditionLabel;
                } else {
                    strConditionLabels += RMP_SEMICOLON + strConditionLabel;
                }
            }

            if (dProbabilityOfAllIfBranches > 1.0) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(usageProfilePart);
                throw new RMPIIMException(RMPIMessageResource.IMM_USAGE_PROFILE_PART_INVALID_BRANCHING_CONDITION_PROBABILITIES, strConditionLabels, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (oBranchingStructure.getElsePart() != null) {
                verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oBranchingStructure.getElsePart(), trackedFullProvidedServices);
            }
        } else if (callPropagation.isSetLoopingStructure()) {
            LoopingStructureType oLoopingStructure = callPropagation.getLoopingStructure();
            String strLoopCount = oLoopingStructure.getLoopCount();
            if ((getUsageProfilePartLCAverage(usageProfilePart, strLoopCount) == null) && (getUsageProfilePartLCProbabilityDistribution(usageProfilePart, strLoopCount) == null)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(usageProfilePart);
                throw new RMPIIMException(RMPIMessageResource.IMM_USAGE_PROFILE_PART_MISSING_LOOP_COUNT_LABEL, strLoopCount, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oLoopingStructure.getLoopingPart(), trackedFullProvidedServices);
        } else if (callPropagation.isSetParallelStructure()) {
            ParallelStructureType oParallelStructure = callPropagation.getParallelStructure();
            CallPropagationType[] oParallelParts = oParallelStructure.getParallelPartArray();
            for (CallPropagationType oParallelPart : oParallelParts) {
                verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oParallelPart, trackedFullProvidedServices);
            }
        } else if (callPropagation.isSetRetryStructure()) {
            RetryStructureType oRetryStructure = callPropagation.getRetryStructure();

            verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oRetryStructure.getRetryPart(), trackedFullProvidedServices);
        } else if (callPropagation.isSetMultiTryCatchStructure()) {
            MultiTryCatchStructureType oMultiTryCatchStructure = callPropagation.getMultiTryCatchStructure();

            CallPropagationType[] oMultiTryCatchParts = oMultiTryCatchStructure.getMultiTryCatchPartArray();
            for (CallPropagationType oMultiTryCatchPart : oMultiTryCatchParts) {
                verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oMultiTryCatchPart, trackedFullProvidedServices);
            }
        } else if (callPropagation.isSetCallingActivity()) {
            CallingActivityType oCallingActivity = callPropagation.getCallingActivity();
            String calledServiceName = oCallingActivity.getCalledService();

            ComponentConnectorType oComponentConnector = getComponentConnectorFor(componentInstanceName, calledServiceName);
            if (oComponentConnector == null) {
                throw new RMPIIMException(RMPIMessageResource.IMM_USER_INTERFACE_MISSING_COMPONENT_CONNECTOR, componentInstanceName + RMP_DOT + calledServiceName, usageProfilePart.getForUserInterface(), false);
            }

            String strFromComponentInstance = oComponentConnector.getFromService().getFromComponentInstance();
            String strFromProvidedServiceName = oComponentConnector.getFromService().getFromProvidedService();

            verifyBranchingConditionsAndLoopingCountsAndRecursiveCalls(usageProfilePart, strFromComponentInstance, strFromProvidedServiceName, trackedFullProvidedServices);

        } else if (callPropagation.isSetSequentialStructure()) {
            CallPropagationType[] oSequentialParts = callPropagation.getSequentialStructure().getSequentialPartArray();
            for (CallPropagationType oSequentialPart : oSequentialParts) {
                verifyBranchingConditionsAndLoopingCounts(usageProfilePart, componentInstanceName, oSequentialPart, trackedFullProvidedServices);
            }
        }
    }

    /**
     * Verify component instances in a given system architecture.
     *
     * @param systemArchitecture the given system architecture.
     * @return a hash map of component instance name-component name mappings.
     * @throws RMPIIMException if component instances in the given system architecture are invalid.
     */
    private HashMap<String, String> verifyComponentInstances(SystemArchitectureType systemArchitecture) throws RMPIIMException {
        HashMap<String, String> oListOfComponentInstanceInfo = new HashMap<String, String>();

        ComponentInstanceType[] oComponentInstances = systemArchitecture.getComponentInstanceArray();
        for (ComponentInstanceType oComponentInstance : oComponentInstances) {
            String strComponentInstanceName = oComponentInstance.getName();

            if (oListOfComponentInstanceInfo.containsKey(strComponentInstanceName)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentInstance);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_COMPONENT_INSTANCE_IN_SYSTEM_ARCHITECTURE, strComponentInstanceName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            String strComponentName = oComponentInstance.getForComponent();
            ComponentType oComponent = getComponentByName(strComponentName);
            if (oComponent == null) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentInstance);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_COMPONENT_IN_SYSTEM_ARCHITECTURE, strComponentInstanceName, strComponentName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfComponentInstanceInfo.put(strComponentInstanceName, strComponentName);
        }

        return oListOfComponentInstanceInfo;
    }

    /**
     * Verify component connectors in a given system architecture.
     *
     * @param systemArchitecture    the given system architecture.
     * @param componentInstanceInfo hash map of component instance name-component name mappings.
     * @throws RMPIIMException if the component connectors in the given system architecture are invalid.
     */
    private void verifyComponentConnectors(SystemArchitectureType systemArchitecture, HashMap<String, String> componentInstanceInfo) throws RMPIIMException {
        List<String> oListOfComponentComponentConnectorNames = new ArrayList<String>();

        ComponentConnectorType[] oComponentConnectors = systemArchitecture.getComponentConnectorArray();
        for (ComponentConnectorType oComponentConnector : oComponentConnectors) {
            String strForComponentInstanceName = oComponentConnector.getForService().getForComponentInstance();
            String strForRequiredServiceName = oComponentConnector.getForService().getForRequiredService();

            String strComponentConnectorName = strForComponentInstanceName + RMP_DOT + strForRequiredServiceName;
            if (oListOfComponentComponentConnectorNames.contains(strComponentConnectorName)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentConnector);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_COMPONENT_CONNECTOR_IN_SYSTEM_ARCHITECTURE, strForRequiredServiceName, strForComponentInstanceName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            if (!componentInstanceInfo.containsKey(strForComponentInstanceName)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentConnector);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_COMPONENT_INSTANCE_IN_SYSTEM_ARCHITECTURE, strForComponentInstanceName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            String strComponentName1 = componentInstanceInfo.get(strForComponentInstanceName);
            if (getRequiredService(strComponentName1, strForRequiredServiceName) == null) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentConnector);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_EXISTING_REQUIRED_SERVICE_IN_COMPONENT_CONNECTOR_IN_SYSTEM_ARCHITECTURE, strForRequiredServiceName, strForComponentInstanceName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            String strFromComponentInstanceName = oComponentConnector.getFromService().getFromComponentInstance();
            if (!componentInstanceInfo.containsKey(strFromComponentInstanceName)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentConnector);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_COMPONENT_INSTANCE_IN_SYSTEM_ARCHITECTURE, strFromComponentInstanceName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            String strFromProvidedServiceName = oComponentConnector.getFromService().getFromProvidedService();
            String strComponentName2 = componentInstanceInfo.get(strFromComponentInstanceName);
            if (getProvidedService(strComponentName2, strFromProvidedServiceName) == null) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oComponentConnector);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_EXISTING_PROVIDED_SERVICE_IN_COMPONENT_CONNECTOR_IN_SYSTEM_ARCHITECTURE, strFromProvidedServiceName, strFromComponentInstanceName, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfComponentComponentConnectorNames.add(strComponentConnectorName);
        }
    }

    /**
     * Verify for looping count averages and probability distributions in a given usage profile part.
     *
     * @param usageProfilePart a given usage profile part.
     * @throws RMPIIMException if looping count averages and probability distributions in the given usage profile are invalid.
     */
    private void verifyLCAveragesAndLCProbabilityDistributions(UsageProfilePartType usageProfilePart) throws RMPIIMException {
        List<String> oListOfLCLabels = new ArrayList<String>();

        LCAverage[] oLCAverages = usageProfilePart.getLCAverageArray();
        for (LCAverage oLCAverage : oLCAverages) {
            String strLCAverageLabel = oLCAverage.getLabel();

            if (oListOfLCLabels.contains(strLCAverageLabel)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oLCAverage);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_LOOP_COUNT_LABEL_IN_USAGE_PROFILE_PART, strLCAverageLabel, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfLCLabels.add(strLCAverageLabel);
        }

        UsageProfilePartType.LCProbabilityDistribution[] oLCProbDistributions = usageProfilePart.getLCProbabilityDistributionArray();
        for (UsageProfilePartType.LCProbabilityDistribution oLCProbabilityDistribution : oLCProbDistributions) {
            String strLCPDLabel = oLCProbabilityDistribution.getLabel();
            if (oListOfLCLabels.contains(strLCPDLabel)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oLCProbabilityDistribution);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_LOOP_COUNT_LABEL_IN_USAGE_PROFILE_PART, strLCPDLabel, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            double dEpsilon = 0.000000001;
            double dTotalProbability = 0.0;
            List<Integer> oListOfForLCValues = new ArrayList<Integer>();
            UsageProfilePartType.LCProbabilityDistribution.ForLCValue[] oForLCValues = oLCProbabilityDistribution.getForLCValueArray();
            for (UsageProfilePartType.LCProbabilityDistribution.ForLCValue oForLCValue : oForLCValues) {
                int iForLCValue = oForLCValue.getForLCValue();

                if (oListOfForLCValues.contains(iForLCValue)) {
                    XmlLineNumber oXMLLineNumber = getXMLLineNumber(oForLCValue);
                    throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_LOOP_COUNT_VALUE_IN_USAGE_PROFILE_PART, String.valueOf(iForLCValue), String.valueOf(oXMLLineNumber.getLine()), false);
                }

                oListOfForLCValues.add(iForLCValue);

                dTotalProbability += oForLCValue.getProbability();

            }

            if (dTotalProbability < (1.0 - dEpsilon) || dTotalProbability > (1.0 + dEpsilon)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oLCProbabilityDistribution);
                throw new RMPIIMException(RMPIMessageResource.IMM_INVALID_LOOP_COUNT_PROBABILITY_DISTRIBUTION_IN_USAGE_PROFILE_PART, strLCPDLabel, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfLCLabels.add(strLCPDLabel);
        }
    }

    /**
     * Verify for branching condition probabilities in a given usage profile part.
     *
     * @param usageProfilePart a given usage profile part.
     * @throws RMPIIMException if branching condition probabilities in the given usage profile part are invalid.
     */
    private void verifyProbabilities(UsageProfilePartType usageProfilePart) throws RMPIIMException {
        List<String> oListOfBCProbabilityLabels = new ArrayList<String>();
        BCProbability[] oBCProbabilities = usageProfilePart.getBCProbabilityArray();

        for (BCProbability oBCProbability : oBCProbabilities) {
            String strBCProbabilityLabel = oBCProbability.getLabel();

            if (oListOfBCProbabilityLabels.contains(strBCProbabilityLabel)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oBCProbability);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_PROBABILITY_IN_USAGE_PROFILE_PART, strBCProbabilityLabel, String.valueOf(oXMLLineNumber.getLine()), false);
            }

            oListOfBCProbabilityLabels.add(strBCProbabilityLabel);
        }
    }

    /**
     * Verify services of the input model.
     *
     * @throws RMPIIMException if the services of the input model are invalid.
     */
    private void verifyServices() throws RMPIIMException {
        ServiceType[] oServices = getServices();
        List<String> oListOfServiceNames = new ArrayList<String>();

        for (ServiceType oService : oServices) {
            String strServiceName = oService.getName();

            if (oListOfServiceNames.contains(strServiceName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_SERVICE, strServiceName, false);
            }

            verifyHandledFailures(oService.getSignaledFailures());
            oListOfServiceNames.add(strServiceName);
        }
    }

    /**
     * Verify components of the input model.
     *
     * @throws RMPIIMException if the components of the input model are invalid.
     */
    private void verifyComponents() throws RMPIIMException {
        ComponentType[] oComponents = getComponents();
        List<String> oListOfComponents = new ArrayList<String>();

        for (ComponentType oComponent : oComponents) {
            String strComponentName = oComponent.getName();

            if (oListOfComponents.contains(strComponentName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_COMPONENT, strComponentName, false);
            }

            List<String> oRequiredServiceNames = verifyRequiredServices(oComponent);
            List<String> oProvidedServiceNames = verifyProvidedServices(oComponent);
            verifyServiceImplementations(oComponent, oProvidedServiceNames, oRequiredServiceNames);

            oListOfComponents.add(strComponentName);
        }
    }

    /**
     * Verify required services in a given component.
     *
     * @param component the given component.
     * @return a list of required service names.
     * @throws RMPIIMException if the required services in the given component are invalid.
     */
    private List<String> verifyRequiredServices(ComponentType component) throws RMPIIMException {
        String strComponentName = component.getName();

        RequiredService[] oRequiredServices = component.getRequiredServiceArray();
        List<String> oListOfRequiredServiceNames = new ArrayList<String>();

        for (RequiredService requiredService : oRequiredServices) {
            String strRequiredServiceName = requiredService.getFromService();

            if (oListOfRequiredServiceNames.contains(strRequiredServiceName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_REQUIRED_SERVICE_IN_COMPONENT, strRequiredServiceName, strComponentName, false);
            }

            ServiceType oService = getServiceByName(strRequiredServiceName);
            if (oService == null) {
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_REQUIRED_SERVICE_IN_COMPONENT, strRequiredServiceName, strComponentName, false);
            }

            oListOfRequiredServiceNames.add(strRequiredServiceName);
        }

        return oListOfRequiredServiceNames;
    }

    /**
     * Verify provided services in a given component.
     *
     * @param component the given component.
     * @return a list of provided service names.
     * @throws RMPIIMException if the provided services in the given component are invalid.
     */
    private List<String> verifyProvidedServices(ComponentType component) throws RMPIIMException {
        String strComponentName = component.getName();

        ComponentType.ProvidedService[] oProvidedServices = component.getProvidedServiceArray();
        List<String> oListOfProvidedServiceNames = new ArrayList<String>();

        for (ComponentType.ProvidedService providedService : oProvidedServices) {
            String strProvidedServiceName = providedService.getForService();

            if (oListOfProvidedServiceNames.contains(strProvidedServiceName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_PROVIDED_SERVICE_IN_COMPONENT, strProvidedServiceName, strComponentName, false);
            }

            ServiceType oService = getServiceByName(strProvidedServiceName);
            if (oService == null) {
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_DECLARED_PROVIDED_SERVICE_IN_COMPONENT, strProvidedServiceName, strComponentName, false);
            }

            oListOfProvidedServiceNames.add(strProvidedServiceName);
        }

        return oListOfProvidedServiceNames;
    }

    /**
     * Verify user interfaces of the input model.
     *
     * @throws RMPIIMException if the user interfaces of the input model are invalid.
     */
    private void verifyUserInterfaces() throws RMPIIMException {
        UserInterfaceType[] oUserInterfaces = getUserInterfaces();
        List<String> oListOfUserInterfaceNames = new ArrayList<String>();

        for (UserInterfaceType oUserInterface : oUserInterfaces) {
            String strUserInterfaceName = oUserInterface.getName();

            if (oListOfUserInterfaceNames.contains(strUserInterfaceName)) {
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_USER_INTERFACE, strUserInterfaceName, false);
            }

            String strFromComponentInstanceName = oUserInterface.getFromService().getFromComponentInstance();
            ComponentInstanceType oComponentInstance = getComponentInstance(strFromComponentInstanceName);
            if (oComponentInstance == null) {
                throw new RMPIIMException(RMPIMessageResource.IMM_USER_INTERFACE_REFERRING_TO_NON_EXISTING_COMPONENT_INSTANCE, strUserInterfaceName, strFromComponentInstanceName, false);
            }

            String strComponentName = oComponentInstance.getForComponent();
            String strFromProvidedServiceName = oUserInterface.getFromService().getFromProvidedService();
            if (getProvidedService(strComponentName, strFromProvidedServiceName) == null) {
                throw new RMPIIMException(RMPIMessageResource.IMM_USER_INTERFACE_REFERRING_TO_NON_EXISTING_PROVIDED_SERVICE, strUserInterfaceName, strFromProvidedServiceName, strFromComponentInstanceName, false);
            }

            oListOfUserInterfaceNames.add(strUserInterfaceName);
        }
    }

    /**
     * Verify failure types of the input model, and build the failure type mappings.
     *
     * @throws RMPIIMException if the failure types of the input model are invalid.
     */
    private void verifyFailureTypes() throws RMPIIMException {
        m_oPropagatingFailureTypes = new HashSet<String>();
        m_oStoppingFailureTypes = new HashSet<String>();

        FailureTypeType[] oFailureTypes = getFailureTypes();

        for (FailureTypeType oFailureType : oFailureTypes) {
            String strFailureTypeName = oFailureType.getName();

            if (RMP_F0.equals(strFailureTypeName) || RMP_ALL.equals(strFailureTypeName) || RMP_NONE.equals(strFailureTypeName)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oFailureType);
                throw new RMPIIMException(RMPIMessageResource.IMM_NOT_ALLOWED_FAILURE_TYPE, strFailureTypeName, String.valueOf(oXMLLineNumber.getLine()), String.valueOf(oXMLLineNumber.getColumn()), false);
            }

            if (m_oPropagatingFailureTypes.contains(strFailureTypeName) || m_oStoppingFailureTypes.contains(strFailureTypeName)) {
                XmlLineNumber oXMLLineNumber = getXMLLineNumber(oFailureType);
                throw new RMPIIMException(RMPIMessageResource.IMM_DUPLICATED_FAILURE_TYPE, strFailureTypeName, String.valueOf(oXMLLineNumber.getLine()), String.valueOf(oXMLLineNumber.getColumn()), false);
            }

            if (oFailureType.getType() == FailureTypeType.Type.PROPAGATING_FAILURE_TYPE) {
                m_oPropagatingFailureTypes.add(strFailureTypeName);
            } else {
                m_oStoppingFailureTypes.add(strFailureTypeName);
            }
        }
    }

    /**
     * Get a service by its name.
     *
     * @param serviceName the given service name.
     * @return the service with the given name.
     */
    public ServiceType getServiceByName(String serviceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Service[@name='" + serviceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ServiceType) oXmlObjects[0];
    }

    /**
     * Get a component by its name.
     *
     * @param componentName the given component name.
     * @return the component with the given name.
     */
    public ComponentType getComponentByName(String componentName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Component[@name='" + componentName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ComponentType) oXmlObjects[0];
    }

    /**
     * Get the user interface by name
     *
     * @param userInterfaceName the user interface name.
     * @return the user interface.
     */
    public UserInterfaceType getUserInterfaceByName(String userInterfaceName) {
        if (StringUtils.isBlank(userInterfaceName)) {
            return null;
        }

        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:SystemReliabilityModel/tns:SystemArchitecture/tns:UserInterface[@name='" + userInterfaceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (UserInterfaceType) oXmlObjects[0];
    }

    /**
     * Get all the user interfaces.
     *
     * @return all the user interfaces.
     */
    public UserInterfaceType[] getUserInterfaces() {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:SystemReliabilityModel/tns:SystemArchitecture/tns:UserInterface";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (UserInterfaceType[]) oXmlObjects;
    }

    /**
     * Get all the services.
     *
     * @return all the services.
     */
    public ServiceType[] getServices() {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Service";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ServiceType[]) oXmlObjects;
    }

    /**
     * Get all the failure types.
     *
     * @return all the failure types.
     */
    public FailureTypeType[] getFailureTypes() {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:FailureType";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (FailureTypeType[]) oXmlObjects;
    }

    /**
     * Get the system architectures.
     *
     * @return the system architectures.
     */
    public SystemArchitectureType getSystemArchitecture() {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:SystemReliabilityModel/tns:SystemArchitecture";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (SystemArchitectureType) oXmlObjects[0];
    }

    /**
     * Get all the components.
     *
     * @return all the components.
     */
    public ComponentType[] getComponents() {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Component";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ComponentType[]) oXmlObjects;
    }

    /**
     * Get the usage profile.
     *
     * @return the usage profile.
     */
    public UsageProfileType getUsageProfile() {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:SystemReliabilityModel/tns:UsageProfile";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((UsageProfileType) oXmlObjects[0]);
    }

    /**
     * Get a branching condition probability from a usage profile part by a branching condition label.
     *
     * @param usageProfilePart        a usage profile part.
     * @param branchingConditionLabel a branching condition label.
     * @return a branching condition probability.
     */
    public UsageProfilePartType.BCProbability getUsageProfilePartBCProbability(UsageProfilePartType usageProfilePart, String branchingConditionLabel) {
        for (UsageProfilePartType.BCProbability oBCProbability : usageProfilePart.getBCProbabilityArray()) {
            if (oBCProbability.getLabel().equals(branchingConditionLabel)) {
                return oBCProbability;
            }
        }

        return null;
    }

    /**
     * Get a looping count average from a usage profile part by a looping count label.
     *
     * @param usageProfilePart  a usage profile part.
     * @param loopingCountLabel a looping count label.
     * @return a looping count average.
     */
    public UsageProfilePartType.LCAverage getUsageProfilePartLCAverage(UsageProfilePartType usageProfilePart, String loopingCountLabel) {
        for (UsageProfilePartType.LCAverage oLCAverage : usageProfilePart.getLCAverageArray()) {
            if (oLCAverage.getLabel().equals(loopingCountLabel)) {
                return oLCAverage;
            }
        }

        return null;
    }

    /**
     * Get a looping count probability distribution from a usage profile part by a looping count label.
     *
     * @param usageProfilePart  a usage profile part.
     * @param loopingCountLabel a looping count label.
     * @return a looping count probability distribution.
     */
    public UsageProfilePartType.LCProbabilityDistribution getUsageProfilePartLCProbabilityDistribution(UsageProfilePartType usageProfilePart, String loopingCountLabel) {
        for (UsageProfilePartType.LCProbabilityDistribution oLCProbabilityDistribution : usageProfilePart.getLCProbabilityDistributionArray()) {
            if (oLCProbabilityDistribution.getLabel().equals(loopingCountLabel)) {
                return oLCProbabilityDistribution;
            }
        }

        return null;
    }

    /**
     * Get the component connector for a given component instance and a given required service.
     *
     * @param componentInstanceName a component instance name.
     * @param requiredServiceName   a required service name.
     * @return the component connector.
     */
    public ComponentConnectorType getComponentConnectorFor(String componentInstanceName, String requiredServiceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:SystemReliabilityModel/tns:SystemArchitecture/tns:ComponentConnector";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        for (XmlObject oXmlObject : oXmlObjects) {
            ComponentConnectorType oComponentConnectorType = (ComponentConnectorType) oXmlObject;
            ForService oForService = oComponentConnectorType.getForService();
            if (StringUtils.equals(componentInstanceName, oForService.getForComponentInstance())
                    && StringUtils.equals(requiredServiceName, oForService.getForRequiredService())) {

                return oComponentConnectorType;
            }
        }

        return null;
    }

    /**
     * Get the component instance  by a component instance name.
     *
     * @param componentInstanceName a component instance name.
     * @return the component instance.
     */
    public ComponentInstanceType getComponentInstance(String componentInstanceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:SystemReliabilityModel/tns:SystemArchitecture/tns:ComponentInstance[@name='" + componentInstanceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ComponentInstanceType) oXmlObjects[0];
    }

    /**
     * Get the service implementation by a component name and a provided service name.
     *
     * @param componentName       a component name.
     * @param providedServiceName a provided service name.
     * @return the service implementation.
     */
    public ServiceImplementationType getServiceImplementation(String componentName, String providedServiceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Component[@name='" + componentName + "']/tns:ServiceImplementation[@forService='" + providedServiceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ServiceImplementationType) oXmlObjects[0];
    }

    /**
     * Get a required service from a component by from service name.
     *
     * @param componentName   the given component.
     * @param fromServiceName the from service name.
     * @return the required service.
     */
    public RequiredService getRequiredService(String componentName, String fromServiceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Component[@name='" + componentName + "']/tns:RequiredService[@fromService='" + fromServiceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (RequiredService) oXmlObjects[0];
    }

    /**
     * Get a provided service from a component by from service name.
     *
     * @param componentName  the given component.
     * @param forServiceName the from service name.
     * @return the provided service.
     */
    public ComponentType.ProvidedService getProvidedService(String componentName, String forServiceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SystemReliabilityModeling/tns:ComponentReliabilitySpecification/tns:Component[@name='" + componentName + "']/tns:ProvidedService[@forService='" + forServiceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (ComponentType.ProvidedService) oXmlObjects[0];
    }

    /**
     * Get the number of propagating failure types.
     *
     * @return the number of propagating failure types.
     */
    public int getNumberOfPropagatingFailureTypes() {
        return m_oPropagatingFailureTypes.size();
    }

    /**
     * Get the number of stopping failure types.
     *
     * @return the number of stopping failure types.
     */
    public int getNumberOfStoppingFailureTypes() {
        return m_oStoppingFailureTypes.size();
    }

    /**
     * Get the AFS.
     *
     * @return the AFS.
     */
    public Set<Set<String>> getAFS() {
        if (m_oAFS == null) {
            m_oAFS = new HashSet<Set<String>>();
            for (String strStoppingFailureType : m_oStoppingFailureTypes) {
                Set<String> oSet = new HashSet<String>();
                oSet.add(strStoppingFailureType);
                m_oAFS.add(oSet);
            }
        }

        return m_oAFS;
    }

    /**
     * Get the AIOS.
     *
     * @return the AIOS.
     */
    public Set<Set<String>> getAIOS() {
        if (m_oAIOS == null) {
            m_oAIOS = Sets.powerSet(m_oPropagatingFailureTypes);
        }

        return m_oAIOS;
    }

    /**
     * Get all the XML errors.
     *
     * @return all the XML errors.
     */
    public List<XmlError> getXMLErrors() {
        return m_oXMLErrors;
    }

    /**
     * Set whether the manager should verify duplicated internal activity names or not.
     *
     * @param isVerifyingDuplicatedIANames true if the manager should verify, otherwise false.
     */
    public void setIsVerifyingDuplicatedIANames(boolean isVerifyingDuplicatedIANames) {
        m_isVerifyingDuplicatedIANames = isVerifyingDuplicatedIANames;
    }


    /**
     * Set the input model file name.
     *
     * @param inputModelFilename the input model file name.
     * @throws RMPIIMException if there is an error.
     */
    public void setInputModelFilename(String inputModelFilename) throws RMPIIMException {
        try {
            try {
                File oFile = new File(inputModelFilename);

                if (!oFile.exists()) {
                    throw new RMPIIMException(RMPIMessageResource.IMM_NON_EXISTING_INPUT_MODEL_FILE, inputModelFilename, false);
                }

                setInputModel(FileUtils.readFileToString(new File(inputModelFilename)));
                m_isValid = true;
            } catch (IOException oIOEx) {
                throw new RMPIIMException(RMPIMessageResource.IMM_UNEXPECTED_EXCEPTION, oIOEx, false);
            }
        } catch (RMPIIMException oEx) {
            m_isValid = false;
            throw oEx;
        }

    }
}
